package DistSys;

import java.util.*;

import Element.*;
import Element.Geom.*;

public class Templates {

    //used when determining when A and B are close enough
    //(A-B)/B <= e <==> A-B <= eB ( to avoid the case B=0 )
    public static final double EPSILON = 0.0001;

    public static Vector allList;
    public static Vector[] subLists;//elem list of the same type
    public static Vector[] nameLists;   //names of the elems (of the same type)
    // public static Vector[] listCount; //counts # of elements with the same template

    static{
        allList = new Vector(50,50);
        subLists = new Vector[SysConst.MAX_TEMPLTYPE];
        nameLists    = new Vector[SysConst.MAX_TEMPLTYPE];
        for(int i=0;i<SysConst.MAX_TEMPLTYPE;i++){
            subLists[i] = new Vector(10,10);
            nameLists[i] = new Vector(10,10);
        }
    };


    public Templates() { }

    //This will insert one entry into the tempList.
    //If there already exists an entry with the same name,
    //The content in the OLD one will be replaced and returned;
    //Other wise, a new entry will be attached and returned.
    public static AbsElem insertIntoWithReplaceTempList(AbsElem elem){
        String name = elem.getElemGeomObj().getName();
        int sz = allList.size();
        for (int i = 0; i < sz; i++) {
            AbsElem elemOld = (AbsElem)allList.elementAt(i);
            AbsElemGeom geomOld = elemOld.getElemGeomObj();
            if (name.equals(geomOld.getName())) {
                copyAllDataFromTemp(elemOld, elem);

                int type = elem.getType();
                int typeOld = elemOld.getType();
                if (type != typeOld) {
                    subLists[typeOld].remove(elemOld);
                    nameLists[typeOld].remove(geomOld.getName());
                    subLists[type].add(elem);
                    nameLists[type].add(name);
                }
                return elemOld;   //return the old one
            }
        }
        allList.add(elem);  //attach a new entry
        insertIntoSubList(elem); //will be insert anyway
        return elem;  //return the same one as the argument
    }

    public static void insertIntoWithReplaceTempList(Vector v){
        AbsElem elem;
        int sz = v.size();
        for(int i=0; i<sz; i++){
            elem =(AbsElem) v.get(i);
            insertIntoWithReplaceTempList( elem );
        }
    }

    //This will insert one entry into the tempList.
    //If there already exists an entry with the same name,
    //no elem is inserted.
    /*public static boolean insertIntoTempList(AbsElem elem){
        AbsElem e2;
        AbsElemGeom geom = elem.getElemGeomObj();
        boolean isExisted=false;
        int sz = allList.size();
        for(int i=0;i<sz;i++){
            e2 = (AbsElem) allList.elementAt(i);
            if( geom.getName().equals( e2.getElemGeomObj().getName() ) ){
                isExisted = true;
                break;
            }
        }
        if( isExisted==false ){ allList.add(elem); insertIntoSubList(elem); }
        return isExisted;
    }*/

    public static void createSubList(){
        for(int i=0;i<SysConst.MAX_TEMPLTYPE;i++){
            subLists[i].removeAllElements();
            nameLists[i].removeAllElements();
        }
        for(int i=0;i<allList.size();i++){
            insertIntoSubList((AbsElem)allList.elementAt(i)); //totally 7 sublists
        }
    }

    public static void insertIntoSubList(AbsElem elem){
        int type = elem.getType();
        subLists[type].add(elem);
        nameLists[type].add(elem.getElemGeomObj().getName());
    }

    public static void remove(AbsElem elem){
        allList.remove(elem);
        int type = elem.getType();
        subLists[type].remove(elem);
        nameLists[type].remove(elem.getElemGeomObj().getName());
    }

    public static void reset(){
        allList.removeAllElements();
        for(int i=0;i<SysConst.MAX_TEMPLTYPE;i++){
            subLists[i].removeAllElements();
            nameLists[i].removeAllElements();
        }
    }

    public static void divideDataInPerUnitForVector( Vector v, double k) {
        Sys.divideDataInPerUnitForVector( v, k);
    }

    public static void divideDataInPerUnitForAllTmplt( double k) {
        Sys.divideDataInPerUnitForVector( allList, k);
    }

    public static AbsElem mapStringToObject(String name){
        if( name==null) return null;
        int sz = allList.size();
        AbsElem elem;
        for(int i=0;i<sz;i++){
            elem =(AbsElem) allList.elementAt(i);
            if( name.equals(elem.getElemGeomObj().getName())){
                return elem;
            }
        }
        return null;
    }

    public static int findSubListIndex(AbsElem elem){
        for(int i=0;i<SysConst.MAX_TEMPLTYPE;i++){
            if( subLists[i].contains(elem) ) return i;
        }
        return -1;
    }

    /**
     * Do not copy for
     *    1. non-syn. fields
     *    2. Fields with non-default flag.
     */
    public static void copyAllDataFromTemp(AbsElem target, AbsElem source){
        for(int i=0; i< AbsElem.NUM_DATA_FIELD; i++){
            //Non-sync. fields
            if( i==AbsElem.DATA_LENGTH || i==AbsElem.DATA_STUDY_AREA){
                continue;
            }
            if( target.getDftFlag(i) ){
                target.setData( i, source.getData(i));
            }
        }
    }//end of copyAllDataFromTemp();

    /**
     * Check two values to determine whether they are the same
     * (difference smaller enough)
     * (A-B)/B <= e <==> A-B <= eB ( to avoid the case B=0 )
     */
    private static boolean checkSameValues( double dValOne, double dValTwo){
        if( Math.abs( dValOne-dValTwo ) > EPSILON * dValTwo ) { return false; }
        else  { return true; }
    }

    /**
     * check wether elem is synchronized with dftElem
     */
    private static boolean checkSync( AbsElem elem, AbsElem dftElem){
        boolean bIsSync = true;
        String msg ="";
        for(int i=0; i< AbsElem.NUM_DATA_FIELD; i++){
            //Non-sync. fields
            if( i==AbsElem.DATA_LENGTH || i==AbsElem.DATA_STUDY_AREA){
                continue;
            }

            if( elem.getDftFlag(i) &&
                checkSameValues( elem.getData(i), dftElem.getData(i)) == false ){
                    if( i == AbsElem.DATA_LENGTH ) continue;
                    else {
                        msg += "\""+SysConst.getDataFieldFromValueIndex(i)+"\"; ";
                        bIsSync = false;
                    }
            }
        }
        if( bIsSync == false ) { elem.setMsgForLog(msg); }
        return bIsSync;
    }

    /**
     * find all unsync. elem, put them in a Vector and return them this Vector
     */
    public static Vector findUnsyncElem( ){
        //String msg = "Unsynchronized with templates.";
        AbsElem elem;
        AbsElem dftElem;
        Vector v=new Vector();
        int sz = Sys.elemList.size();
        for(int i=0; i<sz; i++){
            elem = (AbsElem) Sys.elemList.get(i);
            dftElem = elem.getDftTemp();
            if( dftElem == null ){
                v.add( elem );
                elem.setMsgForLog( "No associate template.");
            } else if ( checkSync(elem, dftElem)==false ){
                v.add( elem );
                //elem.setMsgForLog( msg );
            }
        }
        return v;
    }

    public static Vector findNoTmpltElem( ){
        String msg = "No associate template.";
        AbsElem elem;
        AbsElem dftElem;
        Vector v=new Vector();
        int sz = Sys.elemList.size();
        for(int i=0; i<sz; i++){
            elem = (AbsElem) Sys.elemList.get(i);
            dftElem = elem.getDftTemp();
            if( dftElem==null){
                v.add( elem );
                elem.setMsgForLog( msg );}
        }
        return v;
    }

    /**
     * Sync. all elements with one template.
     * This takes (N) steps, where N=Sys.elemList.size();
     */
    public static void syncTmpltForOne( AbsElem objTmplt){
        if( objTmplt==null) return;
        AbsElem elem;
        int sz = Sys.elemList.size();
        for(int i=0; i<sz; i++){
            elem = (AbsElem) Sys.elemList.get(i);
            if( objTmplt.equals(elem.getDftTemp()) ){
                copyAllDataFromTemp( elem, objTmplt);
            }
        }
    }

    /**
     * Sync all templates in the Vector.
     * This takes only (N) steps.
     */
    public static void syncTmpltInVector( Vector vTmpltList) throws Exception{
        int sz = Sys.elemList.size();
        AbsElem elem, dftElem;
        for(int i=0; i<sz; i++){
            elem = (AbsElem) Sys.elemList.get(i);
            dftElem = elem.getDftTemp();
            if( dftElem!=null && vTmpltList.contains(dftElem) ) {
                elem.setDftTemp(dftElem);
                copyAllDataFromTemp(elem, dftElem);
            }
        }//end of for loop
    }//end of syncTmplt( Vector v);

    /**
     * sync all elements with associated templates in the system
     * This will take only (N) steps, as opposed to
     * Templates.syncTmplt(Vector v) takes (MxN) steps.
     */
    public static void syncTmpltForAll() throws Exception{
        syncTmpltInVector( Templates.allList );
    }//end of syncAllTmplt()

    /**
     * Sync all elements in the Vector v.
     * This takes only (N) steps.
     */
    public static void syncElemInVector( Vector vElemList) throws Exception{
        int sz = vElemList.size();
        AbsElem elem, dftElem;
        for(int i=0; i<sz; i++){
            elem = (AbsElem) vElemList.get(i);
            dftElem = elem.getDftTemp();
            if( dftElem!=null ) {
                copyAllDataFromTemp(elem, dftElem);
            }
        }//end of for loop
    }//end of syncElemInVector ( Vector v);


    public static String[] mapAllListToStrings(){
        int sz = allList.size();
        String[] str = new String[sz];
        AbsElem elem;
        for(int i=0;i<sz;i++){
            elem = (AbsElem) allList.elementAt(i);
            str[i] = new String( elem.getElemGeomObj().getName() );
        }
        return str;
    }

    public static String[] mapAllListToStringsWithAllNoneOptions(){
        int sz = allList.size();
        String[] str = new String[sz+2];
        AbsElem elem;
        str[0] = new String("All");
        str[1] = new String("None");
        for(int i=2;i<sz+2;i++){
            elem = (AbsElem) allList.elementAt(i-2);
            str[i] = new String( elem.getElemGeomObj().getName() );
        }
        return str;
    }


    //Range of array[i]: [0, allList.size() +2] // None, All + others
    public static void setSelection( int[] array ){
        AbsElem elem;
        AbsElemGeom geom;
        int id;
        int size;
        if( array[0]==0){ //select all
            size = allList.size();
            for(int i=0; i<size; i++){
                elem = (AbsElem)allList.elementAt(i);
                elem.getElemGeomObj().isSelected =true;
            }
            return;
        }

        size = array.length;
        for(int i=0;i<size;i++){
            id = array[i];
            elem = (AbsElem) allList.elementAt(id-2); //0= all, 1=none
            geom = elem.getElemGeomObj();
            geom.isSelected =true;
        }
    }

    public static void clearSelection(){
        AbsElem elem;
        AbsElemGeom geom;
        int id;
        int sz = allList.size();
        for(int i=0;i<sz;i++){
            elem = (AbsElem) allList.elementAt(i);
            geom = elem.getElemGeomObj();
            geom.isSelected =false;
        }
    }
}